#include "hook.h"
#include "fd_manager.h"
#include "fiber.h"
#include "global.h"
#include "io_manager.h"
#include "log.h"
#include "scheduler.h"
#include "timer.h"

#include <asm-generic/errno-base.h>
#include <asm-generic/errno.h>
#include <asm-generic/ioctls.h>
#include <asm-generic/socket.h>
#include <bits/types/struct_timeval.h>
#include <cerrno>
#include <cstdarg>
#include <cstdint>
#include <dlfcn.h>
#include <fcntl.h>
#include <functional>
#include <memory>
#include <sys/types.h>
#include <unistd.h>
#include <utility>


//当前线程是否hook
static thread_local bool t_hook_enable = false;

//返回当前线程是否hook
bool is_hook_enable()
{
    return t_hook_enable;
}
//设置当前线程的hook状态
void set_hook_enable(bool flag)
{
    t_hook_enable = flag;
}

//需要hook的函数
#define HOOK_FUN(XX) \
    XX(sleep)        \
    XX(usleep)       \
    XX(nanosleep)    \
    XX(socket)       \
    XX(connect)      \
    XX(accept)       \
    XX(close)        \
    XX(fcntl)        \
    XX(ioctl)        \
    XX(getsockopt)   \
    XX(setsockopt)   \
    XX(read)         \
    XX(readv)        \
    XX(recv)         \
    XX(recvfrom)     \
    XX(recvmsg)      \
    XX(write)        \
    XX(writev)       \
    XX(send)         \
    XX(sendto)       \
    XX(sendmsg)      


//给函数指针赋值 
void hook_init()
{
    static bool is_inited = false;
    if(is_inited)
    {
        return;
    }

#define XX(name) name ## _f = (name ## _fun)dlsym(RTLD_NEXT, #name);
    //sleep_f = (sleep_fun)dlsym(RTLD_NEXT, "sleep");
    HOOK_FUN(XX);
#undef XX
}

struct _HookIniter
{
    _HookIniter()
    {
        hook_init();
    }
};
//目的是为了在执行main函数之前执行hook_init()
static _HookIniter s_hook_initer;


//条件定时器的条件
struct timer_info
{
    int cancelled = 0; //条件状态
};


//对IO读写的封装
//fd: 文件描述符
//fun: 系统库原函数
//hook_fun_name: 要hook的函数名称
//event: 事件类型
//timeout_so: 超时类型（发送超时 - 接收超时）
//arg: 模板变参 Args 是一个模板参数包，而 && 表示右值引用，用于保持参数的值类别
template<typename OriginFun, typename... Args>
static ssize_t do_io(int fd, OriginFun fun, const char* hook_fun_name, 
        uint32_t event, int timeout_so, Args&&... arg)
{
    //如果不使用hook
    if(!t_hook_enable)
    {
        //调用原始库函数
        return fun(fd, std::forward<Args>(arg)...);
    }

    //获取文件描述符类型
    FdCtx::ptr ctx = FdMgr::GetInstance()->get(fd);
    if(!ctx)
    {
        return fun(fd, std::forward<Args>(arg)...);
    }

    //如果文件描述符已经关闭
    if(ctx->isClose())
    {
        log_error("fd is close, fd:{}, fun name: {}", fd, hook_fun_name);
        errno = EBADE;
        return -1;
    }

    //如果fd不是sock类型，或者用户将fd主动设置为非阻塞(此时用户需要自己处理非阻塞的逻辑)
    if(!ctx->isSocket() || ctx->getUserNonblock())
    {
        return fun(fd, std::forward<Args>(arg)...);
    }

    //获取超时时间
    uint64_t to = ctx->getTimeout(timeout_so);
    //创建条件定时器的条件
    std::shared_ptr<timer_info> tinfo = std::make_shared<timer_info>();

retry:
    ssize_t n = fun(fd, std::forward<Args>(arg)...);
    while(n == -1 && errno == EINTR) //如果被系统调用中断
    {
        n = fun(fd, std::forward<Args>(arg)...);
    }

    if(n == -1 && errno == EAGAIN) //如果fd 资源暂时不可用（例如数据还没发送过来就去读，或者发送数据时发送缓存被占满）
    {
        IOManager* iom = IOManager::GetThis();
        Timer::ptr timer;
        std::weak_ptr<timer_info> winfo(tinfo);

        //如果对应事件设置了超时时间
        if(to != UINT64_MAX)
        {
            //添加一个定时任务
            timer = iom->addConditionTimer(to, [winfo, fd, iom, event](){
                auto t = winfo.lock();
                if(!t || t->cancelled) //如果定时器已经取消或者已经超时
                {
                    return;
                }
                //设置条件为超时
                t->cancelled = ETIMEDOUT;
                //取消事件，唤醒协程
                iom->cancelEvent(fd, IOManager::Event(event));
            }, winfo);
        }

        //将当前协程添加到事件队列中
        int rt = iom->addEvent(fd,IOManager::Event(event));
        if(rt)  //事件添加失败
        {
            log_error("{} addEvent({}, {}) error", hook_fun_name, fd, event);
            if(timer)
            {
                timer->cancel(); //取消定时任务
            }
            return -1;
        }
        else 
        {
            //让出执行时间   等待 epoll_wait唤醒  或者 条件定时器超时唤醒 
            Fiber::YieldToHold();
            
            if(timer)   //epoll_wait唤醒
            {
                timer->cancel();
            }
            if(tinfo->cancelled) //条件定时器超时唤醒
            {
                log_error("{} timeout", hook_fun_name);
                errno = tinfo->cancelled;
                return -1;
            }
            goto retry;
        }
    }

    return n;
}



extern "C"
{
//定义函数指针 并 初始化
#define XX(name) name ## _fun name ## _f = nullptr;
    //sleep_fun sleep_f = nullptr;
    HOOK_FUN(XX);
#undef XX

unsigned int sleep(unsigned int seconds)
{
    //如果不使用hook
    if(!t_hook_enable)
    {
        //调用原始的库函数
        return sleep_f(seconds);
    }
    Fiber::ptr fiber = Fiber::GetThis();
    IOManager* iom = IOManager::GetThis();

    using ptr = void(Scheduler::*)(Fiber::ptr, uint64_t);
    //将IOManager::schedule 强转为 Scheduler::schedule
    //将当前协程在指定时间后添加到协程调度任务中
    iom->addTimer(seconds * 1000, std::bind((ptr)&IOManager::schedule, iom, fiber, 0));

    //将当前协程切换到后台
    Fiber::YieldToHold();
    return 0;
}

int usleep(__useconds_t useconds)
{
    if(!t_hook_enable)
    {
        return sleep_f(useconds);
    }
    Fiber::ptr fiber = Fiber::GetThis();
    IOManager* iom = IOManager::GetThis();

    using ptr = void(Scheduler::*)(Fiber::ptr, uint64_t);
    //将微妙转换为毫秒
    iom->addTimer(useconds / 1000, std::bind((ptr)&IOManager::schedule, iom, fiber, 0));

    Fiber::YieldToHold();
    return 0;
}

int nanosleep(const struct timespec *requested_time, struct timespec *remaining)
{
    if(!t_hook_enable)
    {
        //requested_time 是要暂停的时间，如被信号唤醒，剩余的时间保存到remaining
        return nanosleep_f(requested_time, remaining);
    }
    Fiber::ptr fiber = Fiber::GetThis();
    IOManager* iom = IOManager::GetThis();

    uint64_t timeout_ms = requested_time->tv_sec * 1000 + requested_time->tv_nsec / 1000 / 1000;
    using ptr = void(Scheduler::*)(Fiber::ptr, uint64_t);
    iom->addTimer(timeout_ms, std::bind((ptr)&IOManager::schedule, iom, fiber, 0));

    Fiber::YieldToHold();
    return 0;
}

//将fd添加到FdMgr中管理
int socket(int domain, int type, int protocol)
{
    //如果不使用hook
    if(!t_hook_enable)
    {
        //地址类型  套接字类型  协议类型
        return socket_f(domain, type, protocol);
    }

    int fd = socket_f(domain, type, protocol);
    if(fd == -1)
    {
        return fd;
    }

    FdMgr::GetInstance()->get(fd, true);
    return fd;
}

int connect_with_timeout(int fd, const struct sockaddr *addr, socklen_t addrlen, uint64_t timeout_ms)
{
    if(!t_hook_enable)
    {
        return connect_f(fd, addr, addrlen);
    }

    FdCtx::ptr ctx = FdMgr::GetInstance()->get(fd, false);
    if(!ctx || ctx->isClose())
    {
        log_error("connect fd is close, fd: {}", fd);
        errno = EBADE;
        return -1;
    }

    if(!ctx->isSocket())
    {
        return connect_f(fd, addr, addrlen);
    }

    //用户主动设置为非阻塞(此时用户需要自己处理非阻塞的逻辑)
    if(ctx->getUserNonblock())
    {  
        return connect_f(fd, addr, addrlen);
    }

    int n = connect_f(fd, addr, addrlen);
    if(n == 0)
    {
        return 0;
    }
    else if (n != -1 || errno != EINPROGRESS)  //操作正在进行
    {
        return n;
    }

    IOManager* iom = IOManager::GetThis();
    Timer::ptr timer;
    std::shared_ptr<timer_info> tinfo(new timer_info); //初始化条件
    std::weak_ptr<timer_info> winfo(tinfo);

    if(timeout_ms != UINT64_MAX)
    {
        timer = iom->addConditionTimer(timeout_ms, [winfo, fd, iom](){
            auto t = winfo.lock();
            if(!t || t->cancelled) //如果条件不存在
            {
                return;
            }
            t->cancelled = ETIMEDOUT;
            iom->cancelEvent(fd, IOManager::WRITE); //取消连接
        }, winfo);
    }

    int rt = iom->addEvent(fd, IOManager::WRITE);
    if(rt == 0)
    {
        Fiber::YieldToHold();
        if(timer)   //如果定时器还在，说明是epoll_wait唤醒
        {
            timer->cancel();
        }
        if(tinfo->cancelled)    //定时器超时唤醒
        {
            log_error("connect timeout, fd:{}", fd);
            errno = tinfo->cancelled;
            return -1;
        }
    }
    else    //addEvent error
    {
        if(timer)
        {
            timer->cancel();
        }
        log_error("connect addEvent({}, write) error", fd);
    }

    int error = 0;
    socklen_t len = sizeof(int);
    //获取一下连接状态
    if(-1 == getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len)) {
        log_error("connect getsockopt error");
        return -1;
    }
    if(!error)
    {
        return 0;
    }
    else 
    {
        log_error("connect error, fd:{}, errno:{}", fd, error);
        errno = error;
        return -1;
    }
}

int connect(int fd, const struct sockaddr *addr, socklen_t len)
{
    return connect_with_timeout(fd, addr, len, g_tcp_connect_timeout);
}

int accept(int fd, struct sockaddr *__restrict addr, socklen_t *__restrict addr_len)
{
    int ret = do_io(fd, accept_f, "accept", IOManager::READ, SO_RCVTIMEO, addr, addr_len);
    if(ret > 0) //如果连接成功
    {
        FdMgr::GetInstance()->get(ret, true);
    }
    return ret;
}

int close(int fd)
{
    if(t_hook_enable)
    {
        return close_f(fd);
    }

    FdCtx::ptr ctx = FdMgr::GetInstance()->get(fd, false);
    if(ctx)
    {
        IOManager* iom = IOManager::GetThis();
        if(iom)
        {
            iom->cancelAll(fd); //取消该fd上的所有事件
        }
        FdMgr::GetInstance()->del(fd);
    }
    return close_f(fd);
}

//设置F_SETFL和F_GETFL，用户是否设置非阻塞
int fcntl(int fd, int cmd, ...)
{
    va_list va;         //定义一个va_list可变参数列表
    va_start(va, cmd);  //始化va_list为可变参数列表的第一个参数的地址 - 指向cmd参数后面第一个参数的地址

    //按照可变参数的类型分类
    switch(cmd)
    {
        case F_SETFL: //设置文件描述符
            {
                int flag = va_arg(va, int); //用于获取当前va所指的可变参数并将并将ap指针移向下一可变参数
                va_end(va);     //清理 va_list 变量

                FdCtx::ptr ctx = FdMgr::GetInstance()->get(fd, false);
                if(!ctx || ctx->isClose() || !ctx->isSocket())
                {
                    return fcntl_f(fd, cmd, flag);
                }
                ctx->setUserNonblock(flag & O_NONBLOCK);  //用户是否设置了非阻塞

                if(ctx->getSysNonblock())   //根据系统设定 设置非阻塞
                {
                    flag |= O_NONBLOCK; //设置非阻塞 - sock类型系统默认都是非阻塞
                }
                else 
                {
                    flag &= ~O_NONBLOCK; //取消非阻塞
                }
                return fcntl_f(fd, cmd, flag);
            }
            break;
        case F_GETFL:
            {
                va_end(va);
                int flag = fcntl_f(fd, cmd);
                FdCtx::ptr ctx = FdMgr::GetInstance()->get(fd, false);
                if(!ctx || ctx->isClose() || !ctx->isSocket())
                {
                    return flag;
                }
                if(ctx->getUserNonblock())
                {
                    return flag | O_NONBLOCK;
                }
                else 
                {
                    return flag | ~O_NONBLOCK;
                }
            }
            break;
        case F_DUPFD:
        case F_DUPFD_CLOEXEC:
        case F_SETFD:
        case F_SETOWN:
        case F_SETSIG:
        case F_SETLEASE:
        case F_NOTIFY:    
    #ifdef F_SETPIPE_SZ
        case F_SETPIPE_SZ:
    #endif
            {
                int flag = va_arg(va, int);      //...为整数的情况
                va_end(va);
                return fcntl_f(fd, cmd, flag);
            }
            break;
        case F_GETFD:
        case F_GETOWN:
        case F_GETSIG:
        case F_GETLEASE:
    #ifdef F_GETPIPE_SZ
            case F_GETPIPE_SZ:
    #endif
            {
                va_end(va);
                return fcntl_f(fd, cmd);    //...为空的情况
            }
            break;
        case F_SETLK:
        case F_SETLKW:
        case F_GETLK:
            {
                struct flock* arg = va_arg(va, struct flock*); //...为struct flock
                va_end(va);
                return fcntl_f(fd, cmd, arg);
            }
            break;
        case F_GETOWN_EX:
        case F_SETOWN_EX: //...为struct f_owner_exlock
            {
                struct f_owner_exlock* arg = va_arg(va, struct f_owner_exlock*);
                va_end(va);
                return fcntl_f(fd, cmd, arg);
            }
            break;
        default:
            break;
    }

    va_end(va);
    return fcntl_f(fd, cmd);

}

//设置用户是否非阻塞
int ioctl(int fd, unsigned long request, ...)
{
    va_list va;
    va_start(va, request);
    void* arg = va_arg(va, void*);
    va_end(va);

    //FIONBIO 宏用于设置或获取文件描述符的非阻塞模式标志
    if(FIONBIO == request)
    {
        bool user_nonblock = !!*(int*)arg; //将arg转为1字节
        FdCtx::ptr ctx = FdMgr::GetInstance()->get(fd, false);
        if(!ctx || ctx->isClose() || !ctx->isSocket())
        {
            return ioctl_f(fd, request, arg);
        }
        ctx->setUserNonblock(user_nonblock);
    }
    return ioctl_f(fd, request, arg);
}

int getsockopt(int fd, int level, int optname, void *__restrict optval, socklen_t *__restrict optlen)
{
    return getsockopt_f(fd, level, optname, optval, optlen);
}

//设置超时时间
int setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen)
{
    if(!t_hook_enable)
    {
        return setsockopt_f(fd, level, optname, optval, optlen);
    }

    if(level == SOL_SOCKET)  //如果是关于设置socket的套接字超时时间的
    {
        FdCtx::ptr ctx = FdMgr::GetInstance()->get(fd, false);
        if(ctx)
        {
            const timeval* v = (const timeval*)optval;
            ctx->setTimeout(optname, v->tv_sec * 1000 + v->tv_usec / 1000);
        }
    }
    return setsockopt_f(fd, level, optname, optval, optlen);
}


ssize_t read(int fd, void *buf, size_t nbytes)
{
    return do_io(fd, read_f, "read", IOManager::READ, SO_RCVTIMEO, buf, nbytes);
}
ssize_t readv(int fd, const struct iovec *iovec, int count)
{
    return do_io(fd, readv_f, "readv", IOManager::READ, SO_RCVTIMEO, iovec, count);
}
ssize_t recv(int fd, void *buf, size_t n, int flags)
{
    return do_io(fd, recv_f, "recv", IOManager::READ, SO_RCVTIMEO, buf, n, flags);
}
ssize_t recvfrom(int fd, void *__restrict buf, size_t n, int flags, struct sockaddr *__restrict addr, socklen_t *__restrict addr_len)
{
    return do_io(fd, recvfrom_f, "recvfrom", IOManager::READ, SO_RCVTIMEO, buf, n, flags, addr, addr_len);
}
ssize_t recvmsg(int fd, struct msghdr *message, int flags)
{
    return do_io(fd, recvmsg_f, "recvmsg", IOManager::READ, SO_RCVTIMEO, message, flags);
}


ssize_t write(int fd, const void *buf, size_t n)
{
     return do_io(fd, write_f, "write", IOManager::WRITE, SO_SNDTIMEO, buf, n);
}
ssize_t writev(int fd, const struct iovec *iovec, int count)
{
    return do_io(fd, writev_f, "writev", IOManager::WRITE, SO_SNDTIMEO, iovec, count);
}
ssize_t send(int fd, const void *buf, size_t n, int flags)
{
    return do_io(fd, send_f, "send", IOManager::WRITE, SO_SNDTIMEO, buf, n, flags);
}
ssize_t sendto(int fd, const void *buf, size_t n, int flags, const struct sockaddr *addr, socklen_t addr_len)
{
    return do_io(fd, sendto_f, "sendto", IOManager::WRITE, SO_SNDTIMEO, buf, n, flags, addr, addr_len);
}
ssize_t sendmsg(int fd, const struct msghdr *message, int flags)
{
    return do_io(fd, sendmsg_f, "sendmsg", IOManager::WRITE, SO_SNDTIMEO, message, flags);
}

} //extern "C"